#include "KernelObserver.h"
#include <pthread.h>
#include "../shark/include/Log.h"

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>
#include <linux/netlink.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <termios.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <unistd.h>
#include <time.h>
#include <assert.h>
#include <pthread.h>
#include <alloca.h>
#include <getopt.h>
#include <termios.h>
#include <dirent.h>
#include <sys/vfs.h>
#include <string.h>

#define NETLINK_TEST    26
#define USER_PORT       53
#define MAX_PLOAD      125
#define MSG_LEN        125
#define UEVENT_BUFFER_SIZE 2048

void (*pOnRoleSwitchRequest)(int role );
void (*pOnHidGadgetReadyRequest)(int hidReady );
void (*pOnKernelConnected)(int connected);
void (*pOnCdcNcmStatus)(int role);
pthread_t m_HidgTid;
pthread_t m_RoleSwitchTid;
pthread_t m_NcmStatusTid;
int mMsgConnected = 0;
int mRoleSwitch=-1; //1切换成host
int mNcmStatus=-1;
volatile int hidReadyCount = 0;
volatile int keepHidgReadRunning = 0;
volatile int keepObserverRunning = 1;

#define HID_HEADER_LENGTH           2       // HID信息头长度
static int hidg_fd = -1;
static const unsigned char iAP2CheckBuf[]       = { 0xFF, 0x55, 0x02, 0x00, 0xEE, 0x10 };

typedef struct _user_msg_info {
	struct nlmsghdr hdr;
	char msg[MSG_LEN];
} user_msg_info;

char* start_with(char*data, char* str) {
	if ('\0' == *str)
		return NULL;
	char* pos = data;
	char* sub = str;
	while (*sub == *pos && *sub != '\0') {
		++sub;
		++pos;
	}
	if (*sub == '\0') {
		return pos;
	} else {
		return NULL;
	}
}

static void aprintBuffer(unsigned char buf[], int length,char * tag) {
//*
	char *p = (char*) malloc(length * sizeof(char)*10);
	int offset = 0;
	memset(p, 0, length * 10);
	for (int i = 0; i <length; i++) {
		offset += sprintf(p + offset, " %02x", (unsigned char) buf[i]);
	}
	LOGD( "%s ###buf = %s \n", tag, p);
	free(p);

}

int hid_init(void)
{
	LOGD( "%s:%d\n", __func__, __LINE__);
	hidg_fd = open("/dev/hidg0", O_RDWR);
	if (hidg_fd < 0) {
		LOGD("hid fd open failed= %d\n", hidg_fd);
		return -1;
	}else{
		LOGD("hid fd open succeed= %d\n", hidg_fd);
	}
	return hidg_fd;
//	return -1;
}

int  hid_read(unsigned char*buf,int len)
{
	int ret;
	ret = read(hidg_fd, buf, len);
    LOGD("+++read len=%d",ret);
    return ret;
}

int  hid_write(unsigned char*buf,int len)
{
	int ret;
	ret = write(hidg_fd, buf, len);
    LOGD("+++write len=%d",ret);
    return ret;
}
void *readHidgDeviceThread(void *arg) {
	while( keepObserverRunning ){
		while( keepHidgReadRunning )
		{
			if (hidg_fd < 0)
			{
				int fd = hid_init();
				if(fd > 0)
				{
					unsigned char buf[100] = { 0 };
					memset(buf,0,100);
					int ret= hid_read(buf,sizeof(buf));
					LOGD("NETLINK:  The accessory HidRead ret = %d  \n",ret);
					if(ret > 0){
						aprintBuffer(buf, ret, "HidRead: ");
					}
					//0e 00 ff 55 02 00 ee 10 00 00
					int pkg_len = HID_HEADER_LENGTH+sizeof(iAP2CheckBuf);
					if( ret>0 && ret >= 8)
					{
						//收到了detect数据包，则认为是带hub的车机
						unsigned char data[6]={0};
						memcpy(data,(buf+HID_HEADER_LENGTH),6);
						if(memcmp(data,iAP2CheckBuf,6)==0)
						{
							LOGD("NETLINK:  The accessory is hid gadget device \n");
							mMsgConnected = 0;
							//回复数据
							//send(iAP2CheckBuf, sizeof(iAP2CheckBuf))
							unsigned char respBuf [10] = {0};
							respBuf [0] = 0x02;
							respBuf [1] = 0x00;
							memcpy((respBuf+2),iAP2CheckBuf,sizeof(iAP2CheckBuf));
							hid_write(respBuf, 10);
							close(hidg_fd);
							keepHidgReadRunning = 0;
							hidReadyCount = 0;

							//同时需要等待1s
							LOGD("NETLINK:  The accessory wait connect status . \n");
							usleep(1000*1000);
							LOGD("NETLINK:  The accessory wait connect status mMsgConnected =% d. \n",mMsgConnected);
							if (mMsgConnected == 0 && pOnHidGadgetReadyRequest != NULL) {
								pOnHidGadgetReadyRequest(1);
							}else{
								mMsgConnected = 0;
								LOGD("NETLINK:  The accessory isn't hid gadget device,due to connected come. \n");
							}
						}
					}
				}
			}

			usleep(50*1000);
		}
		usleep(50*1000);
	}
	return NULL;
}
void *roleSwitchThread(void *arg) {
	int skfd;
	int ret;
	user_msg_info u_info;
	socklen_t len;
	struct nlmsghdr *nlh = NULL;
	struct sockaddr_nl saddr, daddr;
	char *umsg = "hello netlink!!";
	int loop_count = 0;
	int fhid = 0;
	int i = 0;
	//char buffer[HID_MAX_PACKET_SIZE];

	/*Create netlink socket*/
	skfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); //Create a socket using user defined protocol NETLINK_TEST.
	if (skfd == -1) {
		LOGD("create socket error\n");
		if(pOnKernelConnected!=NULL)
		{
			pOnKernelConnected(0);
		}
		return NULL;
	}

	//Source address.
	memset(&saddr, 0, sizeof(saddr));
	saddr.nl_family = AF_NETLINK; //AF_NETLINK
	saddr.nl_pid = USER_PORT;  //netlink portid, same as kernel.
	saddr.nl_groups = 0;
	if (bind(skfd, (struct sockaddr *) &saddr, sizeof(saddr)) != 0) //bind to skfd with saddr.
			{
		LOGD("bind() error\n");
		close(skfd);
		if(pOnKernelConnected!=NULL)
		{
			pOnKernelConnected(0);
		}
		return NULL;
	}

	if(pOnKernelConnected!=NULL)
	{
		pOnKernelConnected(1);
	}
	//Destination address.
	memset(&daddr, 0, sizeof(daddr));
	daddr.nl_family = AF_NETLINK;
	daddr.nl_pid = 0;    // to kernel
	daddr.nl_groups = 0;

	nlh = (struct nlmsghdr *) malloc(NLMSG_SPACE(MAX_PLOAD));
	memset(nlh, 0, sizeof(struct nlmsghdr));
	nlh->nlmsg_len = NLMSG_SPACE(MAX_PLOAD);
	nlh->nlmsg_flags = 0;
	nlh->nlmsg_type = 0;
	nlh->nlmsg_seq = 0;
	nlh->nlmsg_pid = saddr.nl_pid; //self port

	memcpy(NLMSG_DATA(nlh), umsg, strlen(umsg));
	sendto(skfd, nlh, nlh->nlmsg_len, 0, (struct sockaddr *) &daddr,
			sizeof(struct sockaddr_nl));
	while (1) {
		usleep(1);
		//Receive netlink message from kernel.
		memset(&u_info, 0, sizeof(u_info));
		len = sizeof(struct sockaddr_nl);
		ret = recvfrom(skfd, &u_info, sizeof(user_msg_info), 0,
				(struct sockaddr *) &daddr, &len);
		if (!ret) {
			LOGD("===recv form kernel error\n");
			close(skfd);
			break;
			//exit(-1);
		}

//		LOGD("++++ from kernel:%s\n", u_info.msg);
		if (strcmp(u_info.msg, "connected") == 0) { //ROLE SWITCH CMD
			LOGD("++++ from kernel:%s\n", u_info.msg);
			keepHidgReadRunning = 0;
			hidReadyCount = 0;
			mMsgConnected = 1;
			if(mRoleSwitch!=1){
				mRoleSwitch=1;
				if (pOnRoleSwitchRequest != NULL) {
					pOnRoleSwitchRequest(1);
				}
			}
		}else if (strcmp(u_info.msg, "disconnected") == 0) { //ROLE SWITCH CMD
			LOGD("++++ from kernel:%s\n", u_info.msg);
			if(mRoleSwitch==1){
				mRoleSwitch=0;
				if (pOnRoleSwitchRequest != NULL) {
					pOnRoleSwitchRequest(0);
					if( hidg_fd >0 )
					{
						hidg_fd = -1;
					}
				}
			}
		}else if (strcmp(u_info.msg, "hidready") == 0) { //hidready
			LOGD("++++ from kernel:%s\n", u_info.msg);
			keepHidgReadRunning = 1;
//			++ hidReadyCount;
//			if(hidReadyCount >= 2 )
//			{
//				keepHidgReadRunning = 1;
//			}

		}
	}
	close(skfd);

	free((void *) nlh);
	return NULL;
}

void *cdcNcmStatusThread(void *arg) {
	struct sockaddr_nl client;
	struct timeval tv;
	int fd, rcvlen, ret;
	fd_set fds;
	int buffersize = 1024;

	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
	memset(&client, 0, sizeof(client));

	client.nl_family = AF_NETLINK;
	client.nl_pid = getpid();
	client.nl_groups = 1; /* receive broadcast message*/

	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize));
	bind(fd, (struct sockaddr*) &client, sizeof(client));

	while (1) {
		char buf[UEVENT_BUFFER_SIZE] = { 0 };
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		tv.tv_sec = 0;
		tv.tv_usec = 100 * 1000;

		ret = select(fd + 1, &fds, NULL, NULL, &tv);
		if (ret < 0)
			continue;

		if (!(ret > 0 && FD_ISSET(fd, &fds)))
			continue;

		/* receive data */
		rcvlen = recv(fd, &buf, sizeof(buf), 0);
		if (rcvlen > 0) {
//			LOGD("NETLINK:  %s\n", buf);
			char * data;
			if (strstr(buf, "usb0") != NULL && (data = start_with(buf, "add")) != NULL) {
				LOGD("NETLINK:  CDC-NCM add \n");
				if(mNcmStatus!=1){
					mNcmStatus=1;
					if (pOnCdcNcmStatus != NULL) {
						pOnCdcNcmStatus(mNcmStatus);
					}
				}
			}else if (strstr(buf, "usb0") != NULL && (data = start_with(buf, "remove")) != NULL){
				LOGD("NETLINK:  CDC-NCM remove \n");
				if(mNcmStatus==1){
					mRoleSwitch=-1;
					mNcmStatus=0;
					if (pOnCdcNcmStatus != NULL) {
						pOnCdcNcmStatus(mNcmStatus);
					}
				}
			}
			/*You can do something here to make the program more perfect!!!*/
		}
	}
	close(fd);
	return NULL;
}

//观察hidg节点情况
void observe_hidg_device(){
	int ret;
	if ((ret = pthread_create(&m_HidgTid, NULL, readHidgDeviceThread, NULL))
			!= 0) {
		LOGD("observe_hidg_device thread create failed!\n");
	} else {
		LOGD("observe_hidg_device thread create successed!\n");
	}
}

//观察驱动请求device->host的切换
void observe_role_switch_request(void (*pFunc)(int role),  void (*pFuncHidGadget)(int hidReady),  void (*pFuncKernelConnected)(int connected)){
	pOnRoleSwitchRequest = pFunc;
	pOnHidGadgetReadyRequest = pFuncHidGadget;
	pOnKernelConnected = pFuncKernelConnected;
	observe_hidg_device();
	int ret;
	if ((ret = pthread_create(&m_RoleSwitchTid, NULL, roleSwitchThread, NULL))
			!= 0) {
		LOGD("observer usb roleSwitch thread create failed!\n");
	} else {
		LOGD("observer usb roleSwitch thread create successed!\n");
	}
}
void observe_cdc_ncm_status(void (*pFunc)(int role)) {
	pOnCdcNcmStatus = pFunc;
	int ret;
	if ((ret = pthread_create(&m_NcmStatusTid, NULL, cdcNcmStatusThread, NULL))
			!= 0) {
		LOGD("observer usb cdcNcmStatus thread create failed!\n");
	} else {
		LOGD("observer usb cdcNcmStatus thread create successed!\n");
	}
}

